package com.societyGames.codeAlong.state.codeExercise.view
{
import com.societyGames.action.IAction;
import com.societyGames.codeAlong.LoadingText;
import com.societyGames.codeAlong.state.codeExercise.model.CaseData;
import com.societyGames.codeAlong.state.codeExercise.model.EditorModel;
import com.societyGames.codeAlong.state.codeExercise.model.ExceptionEvent;
import com.societyGames.codeAlong.state.codeExercise.model.FunctionData;
import com.societyGames.codeAlong.state.codeExercise.model.ModuleData;
import com.societyGames.codeAlong.state.codeExercise.model.SharedData;
import com.societyGames.codeAlong.state.codeExercise.model.SyntaxErrorMessage;
import com.societyGames.codeAlong.state.codeExercise.model.SyntaxErrorMessageEvent;
import com.societyGames.codeAlong.state.codeExercise.model.TermData;
import com.societyGames.codeAlong.state.codeExercise.model.link.IHyperText;
import com.societyGames.codeAlong.state.codeExercise.view.actions.ActionFactory;
import com.societyGames.codeAlong.state.codeExercise.view.exerciseSelect.ExerciseSelectDialog;
import com.societyGames.codeAlong.state.codeExercise.view.libraryTree.LibraryTree;
import com.societyGames.codeAlong.state.codeExercise.view.status.Status;
import com.societyGames.codeAlong.state.codeExercise.view.status.StatusBar;
import com.societyGames.codeAlong.state.codeExercise.view.status.StatusText;
import com.societyGames.codeAlong.state.codeExercise.view.testCase.TestCaseControl;
import com.societyGames.codeAlong.user.PersistentUserData;
import com.societyGames.flashForms.CenterContainer;
import com.societyGames.flashForms.CenterScaleTextFieldContainer;
import com.societyGames.flashForms.DialogManager;
import com.societyGames.flashForms.DockContainer;
import com.societyGames.flashForms.DockType;
import com.societyGames.flashForms.DockUnitType;
import com.societyGames.flashForms.HorizontalSlideContainer;
import com.societyGames.flashForms.HorizontalSlidingDoorContainer;
import com.societyGames.flashForms.HorizontalSplitContainer;
import com.societyGames.flashForms.ItemsContainer;
import com.societyGames.flashForms.ScrollContainer;
import com.societyGames.flashForms.TabItem;
import com.societyGames.flashForms.TabItemsContainer;
import com.societyGames.flashForms.TextButton;
import com.societyGames.flashForms.TextCenterContainer;
import com.societyGames.flashForms.VerticalScrollContainer;
import com.societyGames.flashForms.basicSkin.BasicAssets;
import com.societyGames.flashForms.basicSkin.BasicControls;
import com.societyGames.flashForms.basicSkin.SharedTextFields;
import com.societyGames.system.EventObject;

import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.TextEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
import flash.text.TextFormatAlign;
import flash.text.TextLineMetrics;

public class EditorView extends Sprite
{
  private static const _statusAnimationMilliseconds:int = 500;

  private var _closeEventObject:EventObject;
  private var _userData:PersistentUserData;
  private var _moduleDatas:Vector.<ModuleData>;
  private var _editorModel:EditorModel;
  private var _selectedModuleData:ModuleData;
  private var _caseCount:int; //the total number of cases.

  public function EditorView(userData:PersistentUserData, moduleDatas:Vector.<ModuleData>)
  {
    this._closeEventObject = new EventObject(this, "CLOSE");
    this._userData = userData;
    this._moduleDatas = moduleDatas;
    initializeInterface();
    initializeDirectionArrow();
    initializeToolTip();
  }

  public function setEditorModel(editorModel:EditorModel):void
  {
    if (this._editorModel != null)
    {
      //Unhook handlers
      this._editorModel.testCaseController.testCasesSucceededEventObject.removeEventListener(allTestCasesSucceededHandler);
      this._editorModel.testCaseController.testCasesFailedEventObject.removeEventListener(allTestCasesFailedHandler);
      this._editorModel.testCaseController.selectedCaseChangedEventObject.removeEventListener(selectedTestCaseChangedHandler);
      this._editorModel.syntaxErrorMessageEventObject.removeEventListener(syntaxErrorMessageHandler);
      this._editorModel.exceptionEventObject.removeEventListener(exceptionHandler);
      this._editorModel.unhandledExceptionEventObject.removeEventListener(unhandledExceptionHandler);
      this._editorModel.testCaseController.outputEventObject.removeEventListener(outputHandler);
      this._editorModel.saveFailedEventObject.removeEventListener(saveFailedHandler);

      //Remove the data.
      this._editorModel = null;
    }
    //Clear the old UI
    this._libraryTree.clear();
    this._titleTextField.text = "";
    //this._titleButton.text = "";
    this._goalTextField.text = "";
    this._instructionTextField.htmlText = "";
    this._moduleContainer.item = null;
    this._executeControl.disable();

    //Set the new data
    this._editorModel = editorModel;
    if (this._editorModel != null)
    {
      //Hook handlers.
      this._editorModel.testCaseController.testCasesSucceededEventObject.addEventListener(allTestCasesSucceededHandler);
      this._editorModel.testCaseController.testCasesFailedEventObject.addEventListener(allTestCasesFailedHandler);
      this._editorModel.testCaseController.selectedCaseChangedEventObject.addEventListener(selectedTestCaseChangedHandler);
      this._editorModel.syntaxErrorMessageEventObject.addEventListener(syntaxErrorMessageHandler);
      this._editorModel.exceptionEventObject.addEventListener(exceptionHandler);
      this._editorModel.unhandledExceptionEventObject.addEventListener(unhandledExceptionHandler);
      this._editorModel.testCaseController.outputEventObject.addEventListener(outputHandler);
      this._editorModel.saveFailedEventObject.addEventListener(saveFailedHandler);

      //Initialize UI that needs editorModel.
      var moduleData:ModuleData = this._editorModel.moduleData;
      var sharedData:SharedData = this._editorModel.sharedData;
      this._libraryTree.populateLibrary(moduleData.libraryData);
      var functionDatas:Vector.<FunctionData> = sharedData.functionDatas.slice(0, moduleData.functionIndex);
      functionDatas.sort(
              function compare(a:FunctionData, b:FunctionData):int
              {
                return (a.name == b.name ? 0 : (a.name < b.name) ? -1 : 1);
              }
      );
      this._libraryTree.populateFunctions(functionDatas);
      var termDatas:Vector.<TermData> = sharedData.termDatas.slice(0, moduleData.termIndex);
      termDatas.sort(
              function compare(a:TermData, b:TermData):int
              {
                return (a.term == b.term ? 0 : (a.term < b.term) ? -1 : 1);
              }
      );
      this._libraryTree.populateTerms(termDatas);
      this._caseCount = countCasesIncludingRepeats();
      synchronizeTitle();
      this._instructionTextField.htmlText = this._editorModel.moduleData.hint;
      //Test cases
      this._testCaseControl.setTestCaseController(_editorModel.testCaseController);
      //Module
      if (this._editorModel.testCaseController.selectedTestCase.displayObject != null)
      {
        this._moduleContainer.item = this._editorModel.testCaseController.selectedTestCase.displayObject; //If the test case is already loaded, display it. Otherwise, wait for the event.
      }
      else
      {
        var centerContainer:CenterContainer = new CenterContainer();
        centerContainer.item = new LoadingText();
        this._moduleContainer.item = centerContainer;
      }
      //Enable the UI.
      this._executeControl.setTestCaseController(_editorModel.testCaseController);
      //Grab code if we have any
      if (this._editorModel.userData.currentCode != null)
      {
        this._codeTextField.text = this._editorModel.userData.currentCode;
        this._codeDefaultTextField.refresh();
      }
      //Run any module data commands
      if (this.stage != null)
      {
        runModuleActions();
      }
      else
      {
        this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
      }
    }
    else //No editorModel. Make user select.
    {
      showExerciseSelectDialog(false);
    }
  }

  private function runModuleActions():void
  {
    var moduleData:ModuleData = this._editorModel.moduleData;
    if (moduleData.actionsXML != null)
    {
      var actionsXML:XML = moduleData.actionsXML;
      for each (var actionXml:XML in actionsXML.action)
      {
        var action:IAction = ActionFactory.create(actionXml, this);
        action.startAction();
      }
    }
  }

  private function addedToStageHandler(event:Event):void
  {
    this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
    runModuleActions();
  }

  private function countCasesIncludingRepeats():int
  {
    var caseCount:int = 0;
    for each (var caseData:CaseData in this._editorModel.moduleData.cases)
    {
      caseCount += caseData.repeatCount;
    }
    return caseCount;
  }

  //TODO: Only do this sync once?
  private function synchronizeTitle():void
  {
    this._titleTextField.text = this._editorModel.moduleData.name;
    //this._titleButton.text = this._editorModel.moduleData.name;
    if (this._caseCount > 1)
    {
      this._goalTextField.text = this._editorModel.moduleData.goal + " (" + (this._editorModel.testCaseController.currentTestCaseIndex + 1) + "/" + this._caseCount + ")";
    }
    else
    {
      this._goalTextField.text = this._editorModel.moduleData.goal;
    }
  }

  public function get closeEventObject():EventObject //EditorCloseEvent
  {
    return this._closeEventObject;
  }

  public function get selectedModuleData():ModuleData
  {
    return this._selectedModuleData;
  }

  //EXERCISE----------------------------------------------------------------------------

  private function selectedTestCaseChangedHandler(event:Event):void
  {
    synchronizeTitle();
    this._moduleContainer.item = this._editorModel.testCaseController.selectedTestCase.displayObject;
  }

  private function allTestCasesSucceededHandler(event:Event):void
  {
    showStatus(this._successStatus, "<body>Success</body>", 0x30FF02, "nextButton");
    this._testCaseControl.bounceAllButtons();
  }

  private function showStatus(status:Status, text:String, textColor:int = 0x00000, highlightName:String = null):void
  {
    status.htmlText = text;
    status.highlightName = highlightName;
    this._statusBar.show(status, _statusAnimationMilliseconds);

    this._statusText.htmlText = text;
    this._statusText.show(textColor);

    if (status.highlightName != null)
    {
      highlightByName(status.highlightName);
    }
  }

  private function allTestCasesFailedHandler(event:Event):void
  {
    showFailStatus("Failed Goal", 0xFFC002);
  }

  private function showFailStatus(text:String, textColor:int = 0x000000, highlightName:String = null):void
  {
    showStatus(this._failStatus, "<body>" + text + "</body>", textColor, highlightName);
  }

  //UI EVENTS-------------------------------------------------------------------
  private function moduleDataSelectedHandler(event:Event):void
  {
    var exerciseSelectDialog:ExerciseSelectDialog = ExerciseSelectDialog(event.target);
    exerciseSelectDialog.moduleDataSelectedEventObject.removeEventListener(moduleDataSelectedHandler);
    exerciseSelectDialog.moduleDataSelectedEventObject.removeEventListener(exerciseSelectCancelHandler);
    if (exerciseSelectDialog.selectedModuleData != null && exerciseSelectDialog.selectedModuleData != (this._editorModel != null ? this._editorModel.moduleData : null))
    {
      this._selectedModuleData = exerciseSelectDialog.selectedModuleData;
      close(EditorCloseEnum.selectModuleData);
    }
  }

  private function close(editorCloseEnum:EditorCloseEnum):void
  {
    if (this._editorModel != null)
    {
      this._editorModel.closeCurrentLesson();
    }
    this._closeEventObject.dispatchEvent(new EditorCloseEvent(this._closeEventObject.type, editorCloseEnum));
  }

  private function exerciseSelectCancelHandler(event:Event):void
  {
    var exerciseSelectDialog:ExerciseSelectDialog = ExerciseSelectDialog(event.target);
    exerciseSelectDialog.moduleDataSelectedEventObject.removeEventListener(moduleDataSelectedHandler);
    exerciseSelectDialog.moduleDataSelectedEventObject.removeEventListener(exerciseSelectCancelHandler);
  }

  private function rewindClickHandler(event:Event):void
  {
    this._editorModel.closeCurrentLesson();
    //this._editorModel.testCaseController.resetCurrentTestCase(); //Reset only the current one.
    this._editorModel.testCaseController.reset(); //Reset all.
    this._statusBar.hide();
    this._statusText.hide();
  }

  private function stopClickHandler(event:Event):void
  {
    this._editorModel.stop();

    showFailStatus("Code Stopped");
  }

  private function playClickHandler(event:Event):void
  {
    clearOutput();
    if (this._executeControl.rewindIsEnabled) //If we can rewind, then do that first.
    {
      this._editorModel.testCaseController.reset();
    }
    this._statusBar.hide();
    this._statusText.hide();
    this._editorModel.execute(this._codeTextField.text);
  }

  private function nextClickHandler(event:Event):void
  {
    close(EditorCloseEnum.nextExercise);
  }

  private function linkHandler(event:TextEvent):void
  {
    var parts:Array = event.text.split(":");
    switch (parts[0])
    {
      case "highlight":
        highlightByName(parts[1]);
        break;
      default:
        var hyperText:IHyperText = this._editorModel.linkHash.resolveLink(event.text) as IHyperText;
        if (hyperText != null)
        {
          this._toolTip.hyperText = "<p>" + hyperText.toHyperText() + "</p>";
          this._toolTip.show(this, this.mouseX, this.mouseY);
        }
        else
        {
          throw new ArgumentError("Unknown link command: " + parts[0]);
        }
        break;
    }
  }

  public function highlightByName(name:String):void
  {
    var displayObject:DisplayObject;
    switch (name)
    {
      case "title":
        displayObject = this._goalTextField;
        break;
      case "testCase":
        displayObject = this._moduleContainer;
        break;
      case "testCaseControl":
        displayObject = this._testCaseControl;
        break;
      case "documentation":
        displayObject = this._documentationScrollContainer;
        break;
      case "functions":
        displayObject = _libraryTree.functionsNode;
        break;
      case "classes":
        displayObject = _libraryTree.classesNode;
        break;
      case "codeArea":
        displayObject = this._codeTextField;
        break;
      case "instructionTab":
        displayObject = this._instructionTabItem.headerButton;
        break;
      case "instructions":
        displayObject = this._instructionScrollContainer;
        break;
      case "syntaxErrorTab": //TODO: Remove this
        displayObject = this._outputTabItem.headerButton;
        break;
      case "syntaxError": //TODO: Remove this.
        displayObject = this._outputTextField;
        break;
      case "exceptionTab": //TODO: Remove this.
        displayObject = this._outputTabItem.headerButton;
        break;
      case "exception": //TODO: Remove this.
        displayObject = this._outputTextField;
        break;
      case "resetButton":
        displayObject = this._executeControl.resetButton;
        break;
      case "stopButton":
        displayObject = this._executeControl.stopButton;
        break;
      case "runButton":
        displayObject = this._executeControl.runButton;
        break;
      case "nextButton":
        displayObject = this._executeControl.nextButton;
        break;
      case "outputTab":
        displayObject = this._outputTabItem.headerButton;
        break;
      default:
        throw new ArgumentError("Unknown UI element name: " + name);
    }
    if (displayObject != null)
    {
      this._directionArrow.show(this, displayObject);
    }
  }

  private function saveFailedHandler(event:Event):void
  {
    var windowName:String = "Saving Disabled";
    var hypertext:String = "<body>To re-enable saving of your progress:<br>1) Right click here.<br>2) Click \"Settings...\".<br>3) Click the Folder icon.<br>4) Uncheck \"Never Ask Again\".<br>5) Move the slider to 100kb.</body>";
    var textDialog:TextDialog = new TextDialog(windowName, hypertext);
    textDialog.completeEventObject.addEventListener(saveFailedDialogCompleteHandler);
    DialogManager.instance.showDialog(textDialog)
  }

  /**If we execute and are interrupted by the save failed dialog, when it closes
   * We just run the code again.
   * @param event
   */
  private function saveFailedDialogCompleteHandler(event:Event):void
  {
    TextDialog(event.target).completeEventObject.removeEventListener(saveFailedDialogCompleteHandler);
    this._editorModel.execute(this._codeTextField.text);
  }

  //SYNTAX ERROR----------------------------------------------------------------
  private function syntaxErrorClickedHandler(event:SyntaxErrorMessageEvent):void
  {
    this._codeTextField.showErrorAt(event.syntaxErrorMessage.line);
  }

  private function helpSyntaxErrorClickedHandler(event:SyntaxErrorMessageEvent):void
  {
    var exerciseHyperTexts:Vector.<String> = this._editorModel.getExerciseSyntaxErrorHelp(event.syntaxErrorMessage);
    var hyperText:String = "<p><b>Exercise Specific Help</b>";
    if (exerciseHyperTexts.length > 0)
    {
      for each (var exerciseHyperText:String in exerciseHyperTexts)
      {
        hyperText += "<br>" + exerciseHyperText;
      }
    }
    else
    {
      hyperText += "<br>None.";
    }
    hyperText += "<br><br><b>Error Specific Help</b>";
    var specificHyperText:String = this._editorModel.getSpecificSyntaxErrorHelp(event.syntaxErrorMessage);
    if (specificHyperText != null)
    {
      hyperText += "<br>" + specificHyperText;
    }
    else
    {
      hyperText += "<br>None.";
    }
    hyperText += "<br><br><b>General Help</b>"
      + "<br>Your code has an incorrect sequence of characters. Look for missing, misordered or invalid characters. The \"Line:\" number tells you where the error was detected but sometimes the error is actually on another line.</p>";

    this._toolTip.hyperText = hyperText;
    this._toolTip.show(this, this.mouseX, this.mouseY);
  }

  private function syntaxErrorMessageHandler(event:SyntaxErrorMessageEvent):void
  {
    showSyntaxError(event.syntaxErrorMessage);
  }

  private function showSyntaxError(syntaxErrorMessage:SyntaxErrorMessage):void
  {
    this._outputTextField.syntaxErrorMessage = syntaxErrorMessage;
    this._informationTabItemsContainer.selectTabItem(this._outputTabItem);

    showFailStatus("Syntax Error", 0x000000, "syntaxError");
  }

  //EXCEPTION------------------------------------------------------------------
  private function stackTraceCallClickedHandler(event:StackTraceCallEvent):void
  {
    this._codeTextField.showErrorAt(event.stackTraceCallMessage.lineNumber);
  }

  private function helpExceptionClickedHandler(event:ExceptionEvent):void
  {
    var exerciseHyperTexts:Vector.<String> = this._editorModel.getExerciseExceptionHelp(event.exception);
    var hyperText:String = "<p><b>Exercise Specific Help</b>";
    if (exerciseHyperTexts.length > 0)
    {
      for each (var exerciseHyperText:String in exerciseHyperTexts)
      {
        hyperText += "<br>" + exerciseHyperText;
      }
    }
    else
    {
      hyperText += "<br>None.";
    }
    hyperText += "<br><br><b>Error Specific Help</b>";
    var specificHyperText:String = this._editorModel.getSpecificExceptionHelp(event.exception);
    if (specificHyperText != null)
    {
      hyperText += "<br>" + specificHyperText;
    }
    else
    {
      hyperText += "<br>None.";
    }
    hyperText += "<br><br><b>General Help</b>"
      + "<br>Your code is syntactically correct, but semantically incorrect. Your code tried to do something that doesn't make sense according to the rules of the programming language or the rules of the mission. For example, trying to convert the text \"Hello!\" to a number or trying to buy a negative number of kittens are both nonsensical and may raise an exception.</p>";

    this._toolTip.hyperText = hyperText;
    this._toolTip.show(this, this.mouseX, this.mouseY);
  }

  private function exceptionHandler(event:ExceptionEvent):void
  {
    this._outputTextField.exception = event.exception;
    this._informationTabItemsContainer.selectTabItem(this._outputTabItem);

    showFailStatus("Unhandled Exception", 0xFF2502, "exception");
  }

  private function unhandledExceptionHandler(event:Event):void
  {
    this._outputTextField.showUnknownException();
    this._informationTabItemsContainer.selectTabItem(this._outputTabItem);

    showFailStatus("Unhandled Exception", 0xFF2502, "exception");
  }

  //OUTPUT------------------------------------------------------------------
  private function outputHandler(event:TextEvent):void
  {
    this._outputTextField.appendText(event.text);
    this._informationTabItemsContainer.selectTabItem(this._outputTabItem);
  }

  private function clearOutput():void
  {
    this._outputTextField.text = "";
  }

  //UI------------------------------------------------------------------------
  public override function set width(value:Number):void
  {
    this._mainContainer.width = value;
  }

  public override function set height(value:Number):void
  {
    this._mainContainer.height = value;
  }

  private function showMissionSelectHandler(event:Event):void
  {
    showExerciseSelectDialog(true);
  }

  private function showExerciseSelectDialog(showCancel:Boolean):void
  {
    var exerciseSelectDialog:ExerciseSelectDialog = new ExerciseSelectDialog(showCancel);
    DialogManager.instance.showDialog(exerciseSelectDialog); //NOTE: We showDialog before initing so init can scroll to current exercise at correct size.
    exerciseSelectDialog.initialize(this._userData, this._moduleDatas, this._editorModel != null ? this._editorModel.moduleData : null);
    exerciseSelectDialog.moduleDataSelectedEventObject.addEventListener(moduleDataSelectedHandler);
    exerciseSelectDialog.cancelEventObject.addEventListener(exerciseSelectCancelHandler);
  }

  private function statusBarClickHandler(event:MouseEvent):void
  {
    var highlightName:String = Status(this._statusBar.item).highlightName;
    if (highlightName != null)
    {
      highlightByName(Status(this._statusBar.item).highlightName);
    }
  }

  private var _toolTip:HyperTextToolTip;

  private function initializeToolTip():void
  {
    this._toolTip = new HyperTextToolTip();
    this._toolTip.maximumWidth = 400;
    this._toolTip.maximumHeight = 300;
  }

  private var _directionArrow:DirectionArrow;
  [Embed(source="../../../../../../../assets/InterfaceAssets.swf", symbol="DirectionArrow")]
  private static const _arrowClass:Class;

  [Embed(source="/../assets/BodyFrameTopCorner.png")]
  private static const _bodyFrameTopCorner:Class; //Sprite

  [Embed(source="/../assets/BodyFrameSide.png")]
  private static const _bodyFrameSide:Class; //Sprite

  [Embed(source="/../assets/BodyFrameBottomCorner.png")]
  private static const _bodyFrameBottomCorner:Class; //Sprite

  [Embed(source="/../assets/BodyFrameBottom.png")]
  private static const _bodyFrameBottom:Class; //Sprite

  private function initializeDirectionArrow():void
  {
    this._directionArrow = new DirectionArrow(new _arrowClass(), -51, 0);
  }

  private var _titleTextField:TextField;
  private var _titleButton:TextButton;
  private var _goalTextField:TextField;
  private var _moduleContainer:ModuleContainer;
  private var _executeControl:ExecuteControl;
  private var _codeTextField:CodeTextField;
  private var _codeDefaultTextField:DefaultTextField;
  private var _informationTabItemsContainer:TabItemsContainer;
  private var _instructionTextField:TextField;
  private var _instructionScrollContainer:VerticalScrollContainer;
  private var _instructionTabItem:TabItem;
  private var _outputTextField:OutputTextField;
  private var _outputTabItem:TabItem;
  private var _libraryTree:LibraryTree;
  private var _documentationScrollContainer:ScrollContainer;
  private var _mainContainer:DisplayObject; //The main container.
  private var _statusBar:StatusBar;
  private var _failStatus:Status;
  private var _successStatus:Status;
  private var _statusText:StatusText;
  private var _testCaseControl:TestCaseControl;

  private function initializeInformationTabItemsContainer():void
  {
    //Instructions
    this._instructionTextField = BasicControls.createInstructionTextField();
    this._instructionTextField.addEventListener(TextEvent.LINK, linkHandler);

    _instructionScrollContainer = BasicControls.createVerticalScrollContainer();
    _instructionScrollContainer.pixelsPerScrollClick = 12;
    _instructionScrollContainer.item = this._instructionTextField;

    //Output
    this._outputTextField = new OutputTextField();

    this._outputTextField.linkableFileName = EditorModel.userCodeSourceFile;
    this._outputTextField.exceptionStackCallClickedEventObject.addEventListener(stackTraceCallClickedHandler);
    this._outputTextField.helpExceptionClickedEventObject.addEventListener(helpExceptionClickedHandler);

    this._outputTextField.syntaxErrorClickedEventObject.addEventListener(syntaxErrorClickedHandler);
    this._outputTextField.helpSyntaxErrorClickedEventObject.addEventListener(helpSyntaxErrorClickedHandler);

    var outputScrollContainer:ScrollContainer = BasicControls.createScrollContainer();
    outputScrollContainer.item = this._outputTextField;

    //Tabs
    this._instructionTabItem = BasicControls.createTabItem("INSTRUCTION", _instructionScrollContainer);
    this._outputTabItem = BasicControls.createTabItem("OUTPUT", outputScrollContainer);

    //Tab container
    this._informationTabItemsContainer = BasicControls.createTabItemsContainer();
    this._informationTabItemsContainer.addTabItem(this._instructionTabItem);
    this._informationTabItemsContainer.addTabItem(this._outputTabItem);
    this._informationTabItemsContainer.selectTabItem(this._instructionTabItem);
  }

  private function generateModuleTitleStatusDisplayObject():DisplayObject
  {
    //Module
    this._moduleContainer = new ModuleContainer();

    //Goal
    this._goalTextField = SharedTextFields.createTextField();
    this._goalTextField.autoSize = TextFieldAutoSize.CENTER;
    this._goalTextField.multiline = true;
    this._goalTextField.wordWrap = true;
    this._goalTextField.selectable = false;

    var goalTextFormat:TextFormat = this._goalTextField.defaultTextFormat;
    goalTextFormat.size = SharedTextFields.TEXT_LARGE_SIZE;
    goalTextFormat.align = TextFormatAlign.CENTER;
    //goalTextFormat.bold = true;
    this._goalTextField.defaultTextFormat = goalTextFormat;

    //Status Bar
    this._statusBar = new StatusBar();
    this._statusBar.addEventListener(MouseEvent.CLICK, statusBarClickHandler);

    //Fail Status (not shown initially)
    this._failStatus = new Status(BasicAssets.ToolTipClass);
    this._failStatus.height = 20;

    //Success Status (not shown initially)
    this._successStatus = new Status(BasicAssets.ToolTipClass);
    this._successStatus.height = 20;

    this._statusText = new StatusText();
    this._statusText.autoSize = TextFieldAutoSize.CENTER;
    this._statusText.hide();

    var centerStatusContainer:TextCenterContainer = new TextCenterContainer();
    centerStatusContainer.item = this._statusText;

    _testCaseControl = new TestCaseControl();
    _testCaseControl.spacingWidth = 4;
    _testCaseControl.spacingHeight = 1;

    //Center
    var moduleTitleTestCaseDockContainer:DockContainer = new DockContainer();
    moduleTitleTestCaseDockContainer.addItem(this._goalTextField, DockType.top, DockUnitType.item, 0);
    //moduleTitleTestCaseDockContainer.addItem(this._statusBar, DockType.top, DockUnitType.pixel, 20);
    moduleTitleTestCaseDockContainer.addItem(_testCaseControl, DockType.bottom, DockUnitType.pixel, 30);
    moduleTitleTestCaseDockContainer.addItem(this._moduleContainer, DockType.fill, DockUnitType.percent, 1.0);

    var moduleStatusItemContainer:ItemsContainer = new ItemsContainer();
    moduleStatusItemContainer.addItem(moduleTitleTestCaseDockContainer);
    moduleStatusItemContainer.addItem(centerStatusContainer);

    return moduleStatusItemContainer;
  }

  private function initializeLibraryScrollContainer():void
  {
    //Library
    this._libraryTree = new LibraryTree();
    this._libraryTree.linkEventObject.addEventListener(linkHandler);
    this._documentationScrollContainer = BasicControls.createScrollContainer();
    this._documentationScrollContainer.item = this._libraryTree;
  }

  private function initializeInterface():void
  {
    //Information
    initializeInformationTabItemsContainer();
    //Module
    var moduleTitleStatusDisplayObject:DisplayObject = generateModuleTitleStatusDisplayObject();
    //Library
    initializeLibraryScrollContainer();

    var isBigWidth:Boolean = true;
    //Information/Module/Library
    if (isBigWidth)
    {
      var informationModuleLibrarySlidingDoor:HorizontalSlidingDoorContainer = BasicControls.createHorizontalSlidingDoorContainer();
      informationModuleLibrarySlidingDoor.leftItem = _informationTabItemsContainer;
      informationModuleLibrarySlidingDoor.leftPercent = .45;
      informationModuleLibrarySlidingDoor.centerItem = moduleTitleStatusDisplayObject;
      informationModuleLibrarySlidingDoor.rightItem = _documentationScrollContainer;
      informationModuleLibrarySlidingDoor.rightPercent = .10;
    }
    else
    {
      var informationModuleLibrarySlideContainer:HorizontalSlideContainer = new HorizontalSlideContainer();
      informationModuleLibrarySlideContainer.addItem(_informationTabItemsContainer);
      informationModuleLibrarySlideContainer.addItem(moduleTitleStatusDisplayObject);
      informationModuleLibrarySlideContainer.addItem(_documentationScrollContainer);
    }

    //Code
    this._codeTextField = new CodeTextField();
    this._codeTextField.borderColor = 0x000000;
    this._codeTextField.border = true;

    //Code default
    this._codeDefaultTextField = new DefaultTextField();
    SharedTextFields.setDefaultRender(_codeDefaultTextField);
    SharedTextFields.setDefaultFont(_codeDefaultTextField);

    var defaultTextFieldTextFormat:TextFormat = _codeDefaultTextField.defaultTextFormat;
    defaultTextFieldTextFormat.size = SharedTextFields.TEXT_LARGE_SIZE;
    defaultTextFieldTextFormat.align = TextFormatAlign.CENTER;
    //defaultTextFieldTextFormat.bold = true;
    this._codeDefaultTextField.defaultTextFormat = defaultTextFieldTextFormat;

    this._codeDefaultTextField.textColor = 0x999999;//BasicControls.DARK_COLOR;
    this._codeDefaultTextField.text = "Code Area";
    this._codeDefaultTextField.textField = this._codeTextField;

    var codeDefaultCenterContainer:CenterScaleTextFieldContainer = new CenterScaleTextFieldContainer();
    codeDefaultCenterContainer.mouseEnabled = false;
    codeDefaultCenterContainer.mouseChildren = false;
    codeDefaultCenterContainer.textField = _codeDefaultTextField;

    var codeItemsContainer:ItemsContainer = new ItemsContainer();
    codeItemsContainer.addItem(this._codeTextField);
    codeItemsContainer.addItem(codeDefaultCenterContainer);

    //Execute Control
    this._executeControl = new ExecuteControl();
    this._executeControl.rewindClickEventObject.addEventListener(rewindClickHandler);
    this._executeControl.stopClickEventObject.addEventListener(stopClickHandler);
    this._executeControl.playClickEventObject.addEventListener(playClickHandler);
    this._executeControl.nextClickEventObject.addEventListener(nextClickHandler);

    //Code/ModuleInformation
    var codeModuleInformationSplitContainer:HorizontalSplitContainer = BasicControls.createHorizontalSplitContainer();
    if (isBigWidth)
    {
      codeModuleInformationSplitContainer.topItem = informationModuleLibrarySlidingDoor;
    }
    else
    {
      codeModuleInformationSplitContainer.topItem = informationModuleLibrarySlideContainer;
    }
    codeModuleInformationSplitContainer.bottomItem = codeItemsContainer;
    codeModuleInformationSplitContainer.topPercent = 0.5;

    //Everything Else / Execute
    var infoCodeSplitContainer:HorizontalSplitContainer = BasicControls.createHorizontalSplitContainer();
    infoCodeSplitContainer.topItem = codeModuleInformationSplitContainer;
    infoCodeSplitContainer.bottomItem = this._executeControl;
    infoCodeSplitContainer.topPercent = 0.92;

    //Title
    this._titleTextField = SharedTextFields.createTextField();
    this._titleTextField.selectable = false;
    this._titleTextField.background = true;
    this._titleTextField.backgroundColor = BasicControls.BACKGROUND_COLOR;
    this._titleTextField.text = "";
    var titleLineMetrics:TextLineMetrics = this._titleTextField.getLineMetrics(0);
    var titleTextHeight:Number = titleLineMetrics.height + 4;

    this._titleButton = BasicControls.createTextButton();
    this._titleButton.text = "=";
    this._titleButton.buttonClickEventObject.addEventListener(showMissionSelectHandler);

    var titleButtonTextDockContainer:DockContainer = new DockContainer();
    titleButtonTextDockContainer.addItem(this._titleButton, DockType.left, DockUnitType.pixel, titleTextHeight);
    titleButtonTextDockContainer.addItem(this._titleTextField, DockType.fill, DockUnitType.percent, 1);

    var titleExerciseDockContainer:DockContainer = new DockContainer();
    titleExerciseDockContainer.addItem(titleButtonTextDockContainer, DockType.top, DockUnitType.pixel, titleTextHeight);
    titleExerciseDockContainer.addItem(infoCodeSplitContainer, DockType.fill, DockUnitType.percent, 1.0);

    this._mainContainer = titleExerciseDockContainer;
    this.addChild(this._mainContainer);
  }
}
}